package logica;

import gui.Ventana;
import io.hd.ConfiguracionIO;
import io.hd.GestorLog;
import io.hd.GestorRespaldo;
import io.hd.GestorRespaldoListener;
import io.red.GestorInterfacesRed;
import io.servicios.GestorServicios;
import io.servicios.GestorServiciosListener;
import java.io.File;
import java.io.IOException;
import java.net.SocketException;
import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

/**
 *
 * @author Mariano Zapata
 */
public class Nucleo implements GestorServiciosListener, GestorRespaldoListener {
    
    private Ventana ventana;
    private GestorServicios gestorServicios;
    private NucleoListener listener;
    private GestorInterfacesRed gir;
    
    private static final String PATH_CONFIGURACION= "conf.properties";
    private static final String KEY_PUERTO_RMI= "puertoRMI";
    private static final String KEY_ULTIMO_RESPALDO= "fecha_ultimo_respaldo";
    private static final String KEY_INTERFACE_RED= "nombreInterfaceRed";
    private static final String KEY_TIPO_IP= "tipoIP";
    private static final String BD= "H2";
    private static final String DRIVER= "1.3.158";
    private static final String MODO= "vía puente JDBC";
    private static final String VERSION_SISTEMA= "2.0";
    private static final String FECHA_COMPILACION= "01/01/2012";
    private static final String ERROR_LAF= "Máquina Virtual Java (JVM) incompatible. Se requiere versión 6 o superior.";
    private static final String ERROR_SEGUNDA_INSTANCIA= "Ya se encuentra otra instancia del Servidor funcionando " +
            "en esta máquina.\nPor razones de consistencia de datos no se puede tener en funcionamiento a dos Servidores.";
    private static final String ERROR_RMI= "No se puede iniciar el servicio RMI. " +
            "Intente utilizando un puerto RMI diferente.";
    
    public static final int ESTADO_FUNCIONANDO= 0;
    public static final int IP= 1;
    public static final int PUERTO_RMI= 2;
    public static final int CONEXIONES= 3;
    public static final int MOTOR_BD= 4;
    public static final int DRIVER_BD= 5;
    public static final int MODO_CONEXION= 6;
    public static final int ULTIMO_RESPALDO= 7;
    public static final int ESTADO_DETENIDO= 8;
    public static final int ESTADO_INICIANDO= 9;
    public static final int SEGUNDA_INSTANCIA= 10;
    public static final int PUERTO_RMI_OCUPADO= 11;
    public static final int INTERFACE_RED= 12;
    public static final int ERROR_INTERFAZ_RED= 13;
    
    public Nucleo()
    {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            listener= null;
            ventana= new Ventana(this);
            ventana.setVisible(true);
            gir= new GestorInterfacesRed();
            gestorServicios= new GestorServicios();
        } catch (ClassNotFoundException ex) {this.errorLAF();}
          catch (InstantiationException ex) {this.errorLAF();}
          catch (IllegalAccessException ex) {this.errorLAF();}
          catch (UnsupportedLookAndFeelException ex) {this.errorLAF();}
    }
    
    public void iniciar()
    {
        try {
            this.notificarEventoActualizarGui(ESTADO_INICIANDO, "Iniciando...");
            Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION);
            int tipoIP= Integer.parseInt(conf.getProperty(KEY_TIPO_IP));
            gir.configurar(conf.getProperty(KEY_INTERFACE_RED), tipoIP);
            int puertoRMI= Integer.parseInt(conf.getProperty(KEY_PUERTO_RMI));
            gestorServicios.agregarGestorServiciosListener(this);
            gestorServicios.iniciar(gir.getInetAddress(), puertoRMI);
            this.notificarEventoActualizarGui(ESTADO_FUNCIONANDO, "Funcionando");
            this.notificarEventoActualizarGui(IP, gir.getDireccionIP());
            this.notificarEventoActualizarGui(PUERTO_RMI, conf.getProperty(KEY_PUERTO_RMI));
            this.notificarEventoActualizarGui(INTERFACE_RED, gir.getNombre());
            this.notificarEventoActualizarGui(CONEXIONES, "0");
            this.notificarEventoActualizarGui(MOTOR_BD, BD);
            this.notificarEventoActualizarGui(DRIVER_BD, DRIVER);
            this.notificarEventoActualizarGui(MODO_CONEXION, MODO);
            this.notificarEventoActualizarGui(ULTIMO_RESPALDO, conf.getProperty(KEY_ULTIMO_RESPALDO));
        } catch (ClassNotFoundException ex) {this.notificarEventoError(ex.getMessage());}
          catch (InterruptedException ex) {this.notificarEventoError(ex.getMessage());}
          catch (SQLException ex) {if (ex.getErrorCode() == 90020)
                                       this.notificarEventoActualizarGui(SEGUNDA_INSTANCIA, ERROR_SEGUNDA_INSTANCIA);
                                   else
                                       this.notificarEventoError(ex.getMessage());}
          catch (RemoteException ex) {this.generarNotificacionesDetenerGui();
                                      this.notificarEventoActualizarGui(PUERTO_RMI_OCUPADO, ERROR_RMI);}
          catch (IOException ex) {this.generarNotificacionesDetenerGui();
                                  if (ex instanceof SocketException)
                                      this.notificarEventoActualizarGui(ERROR_INTERFAZ_RED, ex.getMessage());
                                  else
                                      this.notificarEventoError(ex.getMessage());}
    }
    
    public boolean detener(boolean hayFalla) throws InterruptedException, SQLException
    {
        if (gestorServicios.detener(hayFalla))
        {
            gestorServicios.quitarGestorServiciosListener();
            this.generarNotificacionesDetenerGui();
            return true;
        }
        else
            return false;
    }
    
    private void generarNotificacionesDetenerGui()
    {
        this.notificarEventoActualizarGui(ESTADO_DETENIDO, "Detenido");
        this.notificarEventoActualizarGui(IP, "0.0.0.0");
        this.notificarEventoActualizarGui(PUERTO_RMI, "0");
        this.notificarEventoActualizarGui(INTERFACE_RED, "");
        this.notificarEventoActualizarGui(CONEXIONES, "0");
        this.notificarEventoActualizarGui(MOTOR_BD, "");
        this.notificarEventoActualizarGui(DRIVER_BD, "");
        this.notificarEventoActualizarGui(MODO_CONEXION, "");
        this.notificarEventoActualizarGui(ULTIMO_RESPALDO, "__/__/____");
    }
    
    public void configurarParametros(String puertoRMI, String nombreInterfaceRed, int tipoIP)
            throws NumberFormatException, Exception
    {
        int portRMI= Integer.parseInt(puertoRMI);
        if (portRMI < 1025 || portRMI > 65536)
            throw new NumberFormatException();
        if (nombreInterfaceRed == null || nombreInterfaceRed.isEmpty())
            throw new Exception();
        try {
            Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION);
            conf.setProperty(KEY_PUERTO_RMI, puertoRMI);
            conf.setProperty(KEY_INTERFACE_RED, nombreInterfaceRed);
            conf.setProperty(KEY_TIPO_IP, String.valueOf(tipoIP));
            ConfiguracionIO.guardarConfiguracion(conf, PATH_CONFIGURACION);
        } catch (IOException ex) {this.notificarEventoError(ex.getMessage());} //SE GENERA EVENTO PORQUE SE LLAMA DESDE
                                                                               //UN DIÁLOGO Y NO DESDE LA VENTANA.
    }
    
    public String getPuertoRMI() throws IOException
    {
        Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION);
        return conf.getProperty(KEY_PUERTO_RMI);
    }
    
    public List<String> getInterfacesRed() throws SocketException {
        return gir.getInterfacesRed();
    }
    
    public String getNombreInterfaceRed() {
        return gir.getNombre();
    }
    
    public int getTipoIP() {
        return gir.getTipoIP();
    }
    
    public void crearRespaldo(File destino, JDialog dialogo)
    {
        GestorRespaldo gr= new GestorRespaldo(GestorRespaldo.TIPO_CREAR, destino.getAbsolutePath(), dialogo);
        gr.start();
    }
    
    public void cargarRespaldo(File origen, JDialog dialogo)
    {
        GestorRespaldo gr= new GestorRespaldo(GestorRespaldo.TIPO_CARGAR, origen.getAbsolutePath(), dialogo);
        gr.start();
    }
    
    public void guardarFechaRespaldo(String fechaHoy) throws IOException
    {
        Properties conf= ConfiguracionIO.cargarConfiguracion(PATH_CONFIGURACION);
        conf.setProperty(KEY_ULTIMO_RESPALDO, fechaHoy);
        ConfiguracionIO.guardarConfiguracion(conf, PATH_CONFIGURACION);
    }
    
    public static String obtenerVersionSistema() {
        return VERSION_SISTEMA;
    }
    
    public static String obtenerFechaCompilacion() {
        return FECHA_COMPILACION;
    }
    
    public static String obtenerVersionJVM() {
        return System.getProperty("java.version");
    }
    
    private void errorLAF()
    {
        GestorLog.escribirLog(ERROR_LAF);
        JOptionPane.showMessageDialog(null, ERROR_LAF, "Error JVM", JOptionPane.ERROR_MESSAGE);
        System.exit(-1);
    }

    public void agregarNucleoListener(NucleoListener nl) {
        listener= nl;
    }
    
    public void quitarNucleoListener() {
        listener= null;
    }
    
    private void notificarEventoError(String msj)
    {
        try {
            this.detener(true);
        } catch (InterruptedException ex) {this.generarNotificacionesDetenerGui();}
          catch (SQLException ex) {this.generarNotificacionesDetenerGui();}
        if (listener != null)
            listener.nucleoEventoError(msj);
    }
    
    private void notificarEventoActualizarGui(int elemento, String valor)
    {
        if (listener != null)
            listener.nucleoEventoActualizarGui(elemento, valor);
    }

    @Override
    public void gestorRespaldoEventoError(String mensaje) {
        this.notificarEventoError(mensaje);
    }

    @Override
    public void gestorServiciosError(String msj) {
        this.notificarEventoError(msj);
    }

    @Override
    public void gestorServiciosEventoConexion(int cantidad) {
        this.notificarEventoActualizarGui(CONEXIONES, String.valueOf(cantidad));
    }
}
